Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[clang][analyzer] Support ownership_{returns,takes} attributes #98941

Merged
merged 9 commits into from
Jul 24, 2024

Conversation

pskrgag
Copy link
Contributor

@pskrgag pskrgag commented Jul 15, 2024

Add support for checking mismatched ownership_returns/ownership_takes attributes.

Closes #76861

Copy link

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be
notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write
permissions for the repository. In which case you can instead tag reviewers by
name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review
by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate
is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:static analyzer labels Jul 15, 2024
@llvmbot
Copy link
Member

llvmbot commented Jul 15, 2024

@llvm/pr-subscribers-clang-static-analyzer-1

@llvm/pr-subscribers-clang

Author: Pavel Skripkin (pskrgag)

Changes

Add support for checking mismatched ownership_returns/ownership_takes attributes.

Closes #76861


Full diff: https://github.com/llvm/llvm-project/pull/98941.diff

2 Files Affected:

  • (modified) clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp (+168-72)
  • (modified) clang/test/Analysis/MismatchedDeallocator-checker-test.mm (+42)
diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index fe202c79ed620..821e0c8da5d2d 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -103,14 +103,46 @@ using namespace std::placeholders;
 namespace {
 
 // Used to check correspondence between allocators and deallocators.
-enum AllocationFamily {
+enum AllocationFamilyKind {
   AF_None,
   AF_Malloc,
   AF_CXXNew,
   AF_CXXNewArray,
   AF_IfNameIndex,
   AF_Alloca,
-  AF_InnerBuffer
+  AF_InnerBuffer,
+  AF_Custom,
+};
+
+class AllocationFamily {
+public:
+  AllocationFamily(AllocationFamilyKind kind,
+                   std::optional<StringRef> name = std::nullopt)
+      : _kind(kind), _customName(name) {
+    assert(kind != AF_Custom || name != std::nullopt);
+  }
+
+  bool operator==(const AllocationFamily &Other) const {
+    if (Other.kind() == this->kind()) {
+      return this->kind() == AF_Custom ? this->_customName == Other._customName
+                                       : true;
+    } else {
+      return false;
+    }
+  }
+
+  bool operator==(const AllocationFamilyKind &kind) {
+    return this->kind() == kind;
+  }
+
+  bool operator!=(const AllocationFamilyKind &kind) { return !(*this == kind); }
+
+  std::optional<StringRef> name() const { return _customName; }
+  AllocationFamilyKind kind() const { return _kind; }
+
+private:
+  AllocationFamilyKind _kind;
+  std::optional<StringRef> _customName;
 };
 
 } // end of anonymous namespace
@@ -194,7 +226,7 @@ class RefState {
   void Profile(llvm::FoldingSetNodeID &ID) const {
     ID.AddInteger(K);
     ID.AddPointer(S);
-    ID.AddInteger(Family);
+    ID.AddInteger(Family.kind());
   }
 
   LLVM_DUMP_METHOD void dump(raw_ostream &OS) const {
@@ -1670,15 +1702,18 @@ MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallEvent &Call,
   if (!State)
     return nullptr;
 
-  if (Att->getModule()->getName() != "malloc")
-    return nullptr;
+  // Preseve previous behavior when "malloc" class means AF_Malloc
+  auto attrClassName = Att->getModule()->getName();
+  auto AF = attrClassName == "malloc"
+                ? AF_Malloc
+                : AllocationFamily(AF_Custom, attrClassName);
 
   if (!Att->args().empty()) {
     return MallocMemAux(C, Call,
                         Call.getArgExpr(Att->args_begin()->getASTIndex()),
-                        UndefinedVal(), State, AF_Malloc);
+                        UndefinedVal(), State, AF);
   }
-  return MallocMemAux(C, Call, UnknownVal(), UndefinedVal(), State, AF_Malloc);
+  return MallocMemAux(C, Call, UnknownVal(), UndefinedVal(), State, AF);
 }
 
 ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
@@ -1830,16 +1865,18 @@ ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C,
   if (!State)
     return nullptr;
 
-  if (Att->getModule()->getName() != "malloc")
-    return nullptr;
+  // Preseve previous behavior when "malloc" class means AF_Malloc
+  auto attrClassName = Att->getModule()->getName();
+  auto AF = attrClassName == "malloc"
+                ? AF_Malloc
+                : AllocationFamily(AF_Custom, attrClassName);
 
   bool IsKnownToBeAllocated = false;
 
   for (const auto &Arg : Att->args()) {
-    ProgramStateRef StateI =
-        FreeMemAux(C, Call, State, Arg.getASTIndex(),
-                   Att->getOwnKind() == OwnershipAttr::Holds,
-                   IsKnownToBeAllocated, AF_Malloc);
+    ProgramStateRef StateI = FreeMemAux(
+        C, Call, State, Arg.getASTIndex(),
+        Att->getOwnKind() == OwnershipAttr::Holds, IsKnownToBeAllocated, AF);
     if (StateI)
       State = StateI;
   }
@@ -1877,6 +1914,33 @@ static bool didPreviousFreeFail(ProgramStateRef State,
   return false;
 }
 
+static void printOwnershipTakesList(raw_ostream &os, CheckerContext &C,
+                                    const Expr *E) {
+  if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
+    const FunctionDecl *FD = CE->getDirectCallee();
+    if (!FD)
+      return;
+
+    if (FD->hasAttrs()) {
+      bool attrPrinted = false;
+
+      for (const auto *I : FD->specific_attrs<OwnershipAttr>()) {
+        OwnershipAttr::OwnershipKind OwnKind = I->getOwnKind();
+
+        if (OwnKind == OwnershipAttr::Takes) {
+          if (attrPrinted)
+            os << ", ";
+          else
+            os << ", which takes ownership of ";
+
+          os << '\'' << I->getModule()->getName() << "'";
+          attrPrinted = true;
+        }
+      }
+    }
+  }
+}
+
 static bool printMemFnName(raw_ostream &os, CheckerContext &C, const Expr *E) {
   if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
     // FIXME: This doesn't handle indirect calls.
@@ -1918,26 +1982,54 @@ static bool printMemFnName(raw_ostream &os, CheckerContext &C, const Expr *E) {
 
 static void printExpectedAllocName(raw_ostream &os, AllocationFamily Family) {
 
-  switch(Family) {
-    case AF_Malloc: os << "malloc()"; return;
-    case AF_CXXNew: os << "'new'"; return;
-    case AF_CXXNewArray: os << "'new[]'"; return;
-    case AF_IfNameIndex: os << "'if_nameindex()'"; return;
-    case AF_InnerBuffer: os << "container-specific allocator"; return;
-    case AF_Alloca:
-    case AF_None: llvm_unreachable("not a deallocation expression");
+  switch (Family.kind()) {
+  case AF_Malloc:
+    os << "malloc()";
+    return;
+  case AF_CXXNew:
+    os << "'new'";
+    return;
+  case AF_CXXNewArray:
+    os << "'new[]'";
+    return;
+  case AF_IfNameIndex:
+    os << "'if_nameindex()'";
+    return;
+  case AF_InnerBuffer:
+    os << "container-specific allocator";
+    return;
+  case AF_Custom:
+    os << Family.name().value();
+    return;
+  case AF_Alloca:
+  case AF_None:
+    llvm_unreachable("not a deallocation expression");
   }
 }
 
 static void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family) {
-  switch(Family) {
-    case AF_Malloc: os << "free()"; return;
-    case AF_CXXNew: os << "'delete'"; return;
-    case AF_CXXNewArray: os << "'delete[]'"; return;
-    case AF_IfNameIndex: os << "'if_freenameindex()'"; return;
-    case AF_InnerBuffer: os << "container-specific deallocator"; return;
-    case AF_Alloca:
-    case AF_None: llvm_unreachable("suspicious argument");
+  switch (Family.kind()) {
+  case AF_Malloc:
+    os << "free()";
+    return;
+  case AF_CXXNew:
+    os << "'delete'";
+    return;
+  case AF_CXXNewArray:
+    os << "'delete[]'";
+    return;
+  case AF_IfNameIndex:
+    os << "'if_freenameindex()'";
+    return;
+  case AF_InnerBuffer:
+    os << "container-specific deallocator";
+    return;
+  case AF_Custom:
+    os << "function that takes ownership of '" << Family.name().value() << "\'";
+    return;
+  case AF_Alloca:
+  case AF_None:
+    llvm_unreachable("suspicious argument");
   }
 }
 
@@ -2119,9 +2211,10 @@ MallocChecker::FreeMemAux(CheckerContext &C, const Expr *ArgExpr,
 std::optional<MallocChecker::CheckKind>
 MallocChecker::getCheckIfTracked(AllocationFamily Family,
                                  bool IsALeakCheck) const {
-  switch (Family) {
+  switch (Family.kind()) {
   case AF_Malloc:
   case AF_Alloca:
+  case AF_Custom:
   case AF_IfNameIndex: {
     if (ChecksEnabled[CK_MallocChecker])
       return CK_MallocChecker;
@@ -2373,6 +2466,8 @@ void MallocChecker::HandleMismatchedDealloc(CheckerContext &C,
 
         if (printMemFnName(DeallocOs, C, DeallocExpr))
           os << ", not " << DeallocOs.str();
+
+        printOwnershipTakesList(os, C, DeallocExpr);
     }
 
     auto R = std::make_unique<PathSensitiveBugReport>(*BT_MismatchedDealloc,
@@ -3483,53 +3578,54 @@ PathDiagnosticPieceRef MallocBugVisitor::VisitNode(const ExplodedNode *N,
           Sym, "Returned allocated memory");
     } else if (isReleased(RSCurr, RSPrev, S)) {
       const auto Family = RSCurr->getAllocationFamily();
-      switch (Family) {
-        case AF_Alloca:
-        case AF_Malloc:
-        case AF_CXXNew:
-        case AF_CXXNewArray:
-        case AF_IfNameIndex:
-          Msg = "Memory is released";
+      switch (Family.kind()) {
+      case AF_Alloca:
+      case AF_Malloc:
+      case AF_Custom:
+      case AF_CXXNew:
+      case AF_CXXNewArray:
+      case AF_IfNameIndex:
+        Msg = "Memory is released";
+        StackHint = std::make_unique<StackHintGeneratorForSymbol>(
+            Sym, "Returning; memory was released");
+        break;
+      case AF_InnerBuffer: {
+        const MemRegion *ObjRegion =
+            allocation_state::getContainerObjRegion(statePrev, Sym);
+        const auto *TypedRegion = cast<TypedValueRegion>(ObjRegion);
+        QualType ObjTy = TypedRegion->getValueType();
+        OS << "Inner buffer of '" << ObjTy << "' ";
+
+        if (N->getLocation().getKind() == ProgramPoint::PostImplicitCallKind) {
+          OS << "deallocated by call to destructor";
           StackHint = std::make_unique<StackHintGeneratorForSymbol>(
-              Sym, "Returning; memory was released");
-          break;
-        case AF_InnerBuffer: {
-          const MemRegion *ObjRegion =
-              allocation_state::getContainerObjRegion(statePrev, Sym);
-          const auto *TypedRegion = cast<TypedValueRegion>(ObjRegion);
-          QualType ObjTy = TypedRegion->getValueType();
-          OS << "Inner buffer of '" << ObjTy << "' ";
-
-          if (N->getLocation().getKind() == ProgramPoint::PostImplicitCallKind) {
-            OS << "deallocated by call to destructor";
-            StackHint = std::make_unique<StackHintGeneratorForSymbol>(
-                Sym, "Returning; inner buffer was deallocated");
-          } else {
-            OS << "reallocated by call to '";
-            const Stmt *S = RSCurr->getStmt();
-            if (const auto *MemCallE = dyn_cast<CXXMemberCallExpr>(S)) {
-              OS << MemCallE->getMethodDecl()->getDeclName();
-            } else if (const auto *OpCallE = dyn_cast<CXXOperatorCallExpr>(S)) {
-              OS << OpCallE->getDirectCallee()->getDeclName();
-            } else if (const auto *CallE = dyn_cast<CallExpr>(S)) {
-              auto &CEMgr = BRC.getStateManager().getCallEventManager();
-              CallEventRef<> Call =
-                  CEMgr.getSimpleCall(CallE, state, CurrentLC, {nullptr, 0});
-              if (const auto *D = dyn_cast_or_null<NamedDecl>(Call->getDecl()))
-                OS << D->getDeclName();
-              else
-                OS << "unknown";
-            }
-            OS << "'";
-            StackHint = std::make_unique<StackHintGeneratorForSymbol>(
-                Sym, "Returning; inner buffer was reallocated");
+              Sym, "Returning; inner buffer was deallocated");
+        } else {
+          OS << "reallocated by call to '";
+          const Stmt *S = RSCurr->getStmt();
+          if (const auto *MemCallE = dyn_cast<CXXMemberCallExpr>(S)) {
+            OS << MemCallE->getMethodDecl()->getDeclName();
+          } else if (const auto *OpCallE = dyn_cast<CXXOperatorCallExpr>(S)) {
+            OS << OpCallE->getDirectCallee()->getDeclName();
+          } else if (const auto *CallE = dyn_cast<CallExpr>(S)) {
+            auto &CEMgr = BRC.getStateManager().getCallEventManager();
+            CallEventRef<> Call =
+                CEMgr.getSimpleCall(CallE, state, CurrentLC, {nullptr, 0});
+            if (const auto *D = dyn_cast_or_null<NamedDecl>(Call->getDecl()))
+              OS << D->getDeclName();
+            else
+              OS << "unknown";
           }
-          Msg = OS.str();
-          break;
+          OS << "'";
+          StackHint = std::make_unique<StackHintGeneratorForSymbol>(
+              Sym, "Returning; inner buffer was reallocated");
         }
+        Msg = OS.str();
+        break;
+      }
         case AF_None:
           llvm_unreachable("Unhandled allocation family!");
-      }
+        }
 
       // See if we're releasing memory while inlining a destructor
       // (or one of its callees). This turns on various common
diff --git a/clang/test/Analysis/MismatchedDeallocator-checker-test.mm b/clang/test/Analysis/MismatchedDeallocator-checker-test.mm
index 013d677e515cf..ef87951c0131e 100644
--- a/clang/test/Analysis/MismatchedDeallocator-checker-test.mm
+++ b/clang/test/Analysis/MismatchedDeallocator-checker-test.mm
@@ -14,6 +14,13 @@
 void free(void *);
 void __attribute((ownership_takes(malloc, 1))) my_free(void *);
 
+void __attribute((ownership_returns(malloc1))) *my_malloc1(size_t);
+void __attribute((ownership_takes(malloc1, 1))) my_free1(void *);
+
+void __attribute((ownership_returns(malloc2))) *my_malloc2(size_t);
+void __attribute((ownership_returns(malloc2))) *my_malloc3(size_t);
+void __attribute((ownership_takes(malloc2, 1))) __attribute((ownership_takes(malloc3, 1))) my_free23(void *);
+
 //---------------------------------------------------------------
 // Test if an allocation function matches deallocation function
 //---------------------------------------------------------------
@@ -60,6 +67,41 @@ void testMalloc8() {
   operator delete[](p); // expected-warning{{Memory allocated by malloc() should be deallocated by free(), not operator delete[]}}
 }
 
+void testMalloc9() {
+  int *p = (int *)my_malloc(sizeof(int));
+  my_free(p); // no warning
+}
+
+void testMalloc10() {
+  int *p = (int *)my_malloc1(sizeof(int));
+  my_free1(p); // no warning
+}
+
+void testMalloc11() {
+  int *p = (int *)my_malloc2(sizeof(int));
+  my_free23(p); // no warning
+}
+
+void testMalloc12() {
+  int *p = (int *)my_malloc1(sizeof(int));
+  my_free(p); // expected-warning{{Memory allocated by my_malloc1() should be deallocated by function that takes ownership of 'malloc1', not my_free(), which takes ownership of 'malloc'}}
+}
+
+void testMalloc13() {
+  int *p = (int *)my_malloc2(sizeof(int));
+  my_free1(p); // expected-warning{{Memory allocated by my_malloc2() should be deallocated by function that takes ownership of 'malloc2', not my_free1(), which takes ownership of 'malloc1'}}
+}
+
+void testMalloc14() {
+  int *p = (int *)my_malloc1(sizeof(int));
+  my_free23(p); // expected-warning{{Memory allocated by my_malloc1() should be deallocated by function that takes ownership of 'malloc1', not my_free23(), which takes ownership of 'malloc2', 'malloc3'}}
+}
+
+void testMalloc15() {
+  int *p = (int *)my_malloc1(sizeof(int));
+  free(p); // expected-warning{{Memory allocated by my_malloc1() should be deallocated by function that takes ownership of 'malloc1', not free()}}
+}
+
 void testAlloca() {
   int *p = (int *)__builtin_alloca(sizeof(int));
   delete p; // expected-warning{{Memory allocated by alloca() should not be deallocated}}

@pskrgag
Copy link
Contributor Author

pskrgag commented Jul 16, 2024

Hello @steakhal,

could you, please, take a look? I just found linked issue quite interesting and decided to give it a try. I am new to llvm world, so I would really appreciate your review

Copy link
Contributor

@steakhal steakhal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty good. Thanks for working on the issue.
I didn't see any major blocking issue with the PR, so good job on that!

Overall, be sure to follow the llvm coding style, including using upper case variable names.

Thanks again for working on this :)

clang/test/Analysis/MismatchedDeallocator-checker-test.mm Outdated Show resolved Hide resolved
clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp Outdated Show resolved Hide resolved
clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp Outdated Show resolved Hide resolved
clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp Outdated Show resolved Hide resolved
clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp Outdated Show resolved Hide resolved
clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp Outdated Show resolved Hide resolved
clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp Outdated Show resolved Hide resolved
clang/test/Analysis/MismatchedDeallocator-checker-test.mm Outdated Show resolved Hide resolved
clang/test/Analysis/MismatchedDeallocator-checker-test.mm Outdated Show resolved Hide resolved
@llvmbot llvmbot added the clang:frontend Language frontend issues, e.g. anything involving "Sema" label Jul 17, 2024
Copy link
Contributor

@steakhal steakhal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks great! I only had one more thing to test.
Besides that it would be nice to advertise this in the clang/docs/ReleaseNotes.rst.
Usually, from that release notes we also link to the checker or the relevant doc pages - in this case it would be nice to link to the docs of this attribute - which does not really elaborate on the attribute and the semantics.

Do you want to extend the scope of this PR to add some minimal docs to the attribute?
If not, that's also fine, we will create a separate ticket for adding them later.

Comment on lines +27 to +30

int f19(void *)
__attribute__((ownership_takes(foo, 1))) // expected-error {{'ownership_takes' attribute class does not match; here it is 'foo'}}
__attribute__((ownership_takes(foo1, 1))); // expected-note {{declared with class 'foo1' here}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if I want to redeclare the function with a different "ownership_takes" attribute in a subsequent line? Will it detect the conflict?

int f20(void *) __attribute__((ownership_takes(foo, 1)));
int f20(void *) __attribute__((ownership_takes(foo1, 1))); // redecl with different attr

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmmm.... It does not detect. As well as for other attributes.

I think, we should fix it, but maybe all of them in scope of separate PR?

clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp Outdated Show resolved Hide resolved
clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp Outdated Show resolved Hide resolved
clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp Outdated Show resolved Hide resolved
clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp Outdated Show resolved Hide resolved
@pskrgag
Copy link
Contributor Author

pskrgag commented Jul 17, 2024

Do you want to extend the scope of this PR to add some minimal docs to the attribute?
If not, that's also fine, we will create a separate ticket for adding them later.

I would be happy to give it a try, but I think we need to fix couple of things to truly follow the semantics. As you have already noticed, attributes conflicts on different declarations are not reported.

Also I found out that code like

void f21(void)
  __attribute__((ownership_returns(foo)));

does not cause an error. I will fill an issue for these cases.

Also, thank you so much for review!

Copy link
Contributor

@steakhal steakhal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it looks great.
Only a few more stylistic remarks are left, but it should be already in a good shape functional-wise. I'll let you decide if you wanna apply my suggestions or not.

One more remark - this time w.r.t. the workflow. Usually, GitHub PRs prefer "merges" over "force-pushes". Whenever you force-push, all the inline remarks could get lost, as it fails to track the lines where the comments were made. Thus, when working with PRs, one usually just "merges" the tracked branch, and just builds more and more commits on top of your PR branch. Once the review is done, one is free to destroy the history and squash the commits, or do some reordering - because there are no more pending comments or remarks left.

clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp Outdated Show resolved Hide resolved
clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp Outdated Show resolved Hide resolved
clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp Outdated Show resolved Hide resolved
clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp Outdated Show resolved Hide resolved
@pskrgag
Copy link
Contributor Author

pskrgag commented Jul 18, 2024

Thank you for review!

I've applied your suggestions and fixed test failures. Messed up a bit with shadowing in constructor.

Usually, GitHub PRs prefer "merges" over "force-pushes". Whenever you force-push, all the inline remarks could get lost, as it fails to track the lines where the comments were made

Oh, ok. I've never really worked with gh workflow, so didn't know that. Thank you for guidance!

@steakhal steakhal changed the title [Clang SA]: add support for mismatched ownership_returns+ownership_takes calls for custom allocation classes [clang][analyzer] Support ownership_{returns,takes} attributes Jul 18, 2024
@steakhal
Copy link
Contributor

FYI This PR is still not mentioned in the clang/docs/ReleaseNotes.rst.
We should have two entries there:

  1. In Attribute Changes in Clang, mentioning that calls can have at most one ownership_return attribute.
  2. In Static Analyzer > New features, that the MallocChecker now checks this attribute, and you can read the docs for the checker, and the attribute at the links.

@pskrgag
Copy link
Contributor Author

pskrgag commented Jul 18, 2024

and you can read the docs for the checker, and the attribute at the links.

I can't find any docs for this attribute. As I mentioned, I will fill new issues to fix couple of frontend issues and after that we can write down correct semantics of these attrs.

Do you mean that link https://clang.llvm.org/docs/AttributeReference.html#ownership-holds-ownership-returns-ownership-takes?

@steakhal
Copy link
Contributor

and you can read the docs for the checker, and the attribute at the links.

I can't find any docs for this attribute. As I mentioned, I will fill new issues to fix couple of frontend issues and after that we can write down correct semantics of these attrs.

Thats fine.

Do you mean that link https://clang.llvm.org/docs/AttributeReference.html#ownership-holds-ownership-returns-ownership-takes?

Yes. Its generated from a tablegen file. Thats why you probably didnt find it.
Look for files with the "td" extension, and "attr" in name. May have a different casing though. Watch out.

@pskrgag
Copy link
Contributor Author

pskrgag commented Jul 18, 2024

Added release note and added basic example to clang/docs/analyzer/checkers/mismatched_deallocator_example.cpp

Thanks! I learned a lot about llvm workflow. (Ah, again force pushed for some reason, sorry)

pskrgag added 8 commits July 23, 2024 13:48
Add support for custom allocation classes that could be specified by
ownership_returns attribute.

This patch adds basic support, so new class is not contructed anywhere
and handled as an error everywhere.
Patch introduces support for custom allocation classes in
MismatchedDeallocator.

Patch preserves previous behavior, in which 'malloc' class was handled
as AF_Malloc type.
@pskrgag
Copy link
Contributor Author

pskrgag commented Jul 23, 2024

Rebased on top of d89f3e8

@steakhal
Copy link
Contributor

Should I merge this PR, or you want to wait some?

@pskrgag
Copy link
Contributor Author

pskrgag commented Jul 23, 2024

I think, it's good time to merge, since v19 release branch was created.

If you want to wait for others to review, it's also fine.

@steakhal steakhal merged commit 893a303 into llvm:main Jul 24, 2024
6 checks passed
Copy link

@pskrgag Congratulations on having your first Pull Request (PR) merged into the LLVM Project!

Your changes will be combined with recent changes from other authors, then tested
by our build bots. If there is a problem with a build, you may receive a report in an email or a comment on this PR.

Please check whether problems have been caused by your change specifically, as
the builds can include changes from many authors. It is not uncommon for your
change to be included in a build that fails due to someone else's changes, or
infrastructure issues.

How to do this, and the rest of the post-merge process, is covered in detail here.

If your change does cause a problem, it may be reverted, or you can revert it yourself.
This is a normal part of LLVM development. You can fix your changes and open a new PR to merge them again.

If you don't get any reports, no action is required from you. Your changes are working as expected, well done!

yuxuanchen1997 pushed a commit that referenced this pull request Jul 25, 2024
)

Summary:
Add support for checking mismatched ownership_returns/ownership_takes attributes.

Closes #76861

Test Plan: 

Reviewers: 

Subscribers: 

Tasks: 

Tags: 


Differential Revision: https://phabricator.intern.facebook.com/D60250804
Harini0924 pushed a commit to Harini0924/llvm-project that referenced this pull request Aug 1, 2024
…m#98941)

Add support for checking mismatched ownership_returns/ownership_takes attributes.

Closes llvm#76861
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:static analyzer clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Clang SA]: custom malloc/free checker
3 participants